package com.pocket.demo.core.mq.rabbitMQ;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.amqp.RabbitTemplateConfigurer;
import org.springframework.boot.autoconfigure.amqp.RabbitTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author cookie.
 **/
@Configuration
public class RabbitMQConfig{

    private static final Logger LOGGER = LoggerFactory.getLogger(UserRegisterSuccessConsumer.class);

    /**
     * <p>【demo-MQ-rabbitMQ-1.3】
     *     若未导入 spring-boot-start-web 依赖，则需要单独导入 jackson-dataformat-xml。
     * <p>【踩坑】<br>
     *      配置完 Jackson2JsonMessageConverter 后，程序默认会将 message 的 payload 当做 JSON 格式处理。 <br>
     *      如果需要发送字符串格式：<br>
     *      方式一：单个信息单独指定，参考：<br>
     *      方式二：重写 Jackson2JsonMessageConverter 全局指定。<br>
     *      可通过 properties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN) 显式指定消息类型<br>
     *
     * @return
     */
    @Bean
    public MessageConverter messageConverter(){
        // 1.定义消息转换器
        Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter();
        // 2.配置自动创建消息id，用于识别不同消息，也可以在业务中基于ID判断是否是重复消息
        messageConverter.setCreateMessageIds(true);
        return messageConverter;
    }

    /**
     * springboot 自动装配 RabbitTemplate 扩展点
     * 详看 {@link org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration.RabbitTemplateConfiguration#rabbitTemplate(RabbitTemplateConfigurer, ConnectionFactory, ObjectProvider)}
     */
    @Bean
    public RabbitTemplateCustomizer rabbitTemplateCustomizer(){
        return (rabbitTemplate) -> {
            rabbitTemplate.setMessageConverter(messageConverter());
            /*
             * 【demo-MQ-rabbitMQ-6.2】消息投递到 Exchange 后的回调
             *  1. 消息投递到 Exchange 成功，但路由到队列失败，返回 ack = true，路由失败信息通过 Publisher Return 返回
             *  2. 消息投递到 Exchange 成功，路由到队列成功，返回 ack = true
             *  3. 其他情况返回 ack = false
             */
            rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
                if (ack){
                    LOGGER.debug("RabbitMQ: message [{}] send success", correlationData != null ? correlationData.getId() : "");
                } else {
                    // 直接抛出异常，让调用方感知
                    LOGGER.error("RabbitMQ: message [{}] send to exchange ConfirmCallback nack，cause：{}", correlationData != null ? correlationData.getId() : "", cause);
                    throw new AmqpException(cause);
                }
            });
            // 【demo-MQ-rabbitMQ-6.3】消息投递到 Exchange 成功，但路由到队列失败时的回调
            rabbitTemplate.setReturnsCallback(returnMessage -> {
                LOGGER.error("RabbitMQ: message [{}] send to queue ReturnsCallback error，cause：{}", returnMessage.getMessage().getMessageProperties().getMessageId(), returnMessage.getReplyText());
                throw new AmqpException(returnMessage.getReplyText());
            });
        };
    }

}
